﻿using System;
using System.Linq;
using BoldBrick.Core.Logging;
using BoldBrick.OneIntranet.HR.DataObjects;
using BoldBrick.OneIntranet.HR.DataObjects.ContentTypes;
using BoldBrick.OneIntranet.HR.DataObjects.Lists;
using BoldBrick.OneIntranet.HR.Properties;
using BoldBrick.OneIntranet.HR.Resources;
using BoldBrick.SharePoint.CommonControls.Emails;
using BoldBrick.SharePoint.CommonControls.Extensions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Workflow;

namespace BoldBrick.OneIntranet.HR.Workflows
{
	/// <summary>
	/// Helper class for HR workflow
	/// </summary>
	public class HRWorkflowHelper
	{
		private static readonly ILogger log = Logger.GetLogger(typeof (HRWorkflowHelper));

		#region ------ Get Items ------------------------------------------------------------------

		/// <summary>
		/// Gets item of "Workflows" list by item od "Candidates" list bound over "Position" field
		/// </summary>
		/// <param name="web">Parent web of workflow</param>
		/// <param name="itemCandidate">Item of "Candidates" list</param>
		/// <returns>Returns item of "Workflows" list by item od "Candidates" list bound over "Position" field</returns>
		public static SPListItem GetWorkflowItemByCandidateItem(SPWeb web, SPListItem itemCandidate)
		{
			uint language = web.Language;

			// check if position is filled
			SPFieldLookupValue positionValue = itemCandidate.GetValueObject<SPFieldLookupValue>(SiteColumns.PositionLookupColumn.InternalName);
			if (positionValue == null)
			{
				string errorMessage = ResourceBuilder.GetResource("HRWorkflowHelper_Error_NoPosition", language, itemCandidate.Title);
				throw new HRWorkflowException(errorMessage);
			}

			SPList listWorkflows = web.GetList(Lists.WorkflowsList);
			SPQuery positionQuery = GetPositionQuery(positionValue.LookupId);

			SPListItemCollection workflowItems = listWorkflows.GetItems(positionQuery);
			if (workflowItems.Count == 0)
			{
				string errorMessage = ResourceBuilder.GetResource("HRWorkflowHelper_Error_NoWorkflowFound", language, positionValue.LookupValue);
				throw new HRWorkflowException(errorMessage);
			}

			SPListItem itemWorkflow = workflowItems[0];

			return itemWorkflow;
		}

		private static SPQuery GetPositionQuery(int positionID)
		{
			string query = string.Format(
				@"<Where>
					<Eq>
						<FieldRef Name=""{0}"" LookupId=""TRUE"" />
						<Value Type=""Lookup"">{1}</Value>
					</Eq>
				</Where>",
				SiteColumns.WorkflowPositionLookupColumn.InternalName,
				positionID);
			SPQuery spQuery = new SPQuery
			                  	{
			                  		Query = query,
			                  		ViewAttributes = "Scope=\"RecursiveAll\"",
			                  		RowLimit = 1
			                  	};
			return spQuery;
		}

		/// <summary>
		/// Gets all steps of the workflow in order
		/// </summary>
		/// <param name="web">Parent web</param>
		/// <param name="itemWorkflow">Workflow item</param>
		/// <returns>Returns all steps of the workflow in order</returns>
		public static SPListItemCollection GetWorkflowStepItems(SPWeb web, SPListItem itemWorkflow)
		{
			SPList listWorkflow = web.GetList(Lists.WorkflowsList);
			if (listWorkflow == null)
			{
				string errorMessage = ResourceBuilder.GetResource("HRWorkflowHelper_Error_NoWorkflowsListFound", web.Language);
				throw new SPWorkflowException(errorMessage);
			}

			SPQuery spQuery = new SPQuery
			{
				Folder = itemWorkflow.Folder,
				ViewAttributes = "Scope=\"Recursive\"",
				Query = string.Format("<OrderBy><FieldRef Name=\"{0}\" /></OrderBy>", SiteColumns.WorkflowStepOrderColumn.InternalName)
			};

			SPListItemCollection workflowStepItems = listWorkflow.GetItems(spQuery);
			return workflowStepItems;
		}

		#endregion

		#region ------ Workflow Manipulation ------------------------------------------------------

		/// <summary>
		/// Cancels running workflow on item
		/// </summary>
		/// <param name="siteId">ID of parent site</param>
		/// <param name="webId">ID of parent web</param>
		/// <param name="listId">ID of the list of the item</param>
		/// <param name="itemId">ID of the item</param>
		/// <param name="templateWorkflowID">ID of workflow template</param>
		public static void CancelWorkflowElevated(Guid siteId, Guid webId, Guid listId, int itemId, Guid templateWorkflowID)
		{
			SPSecurity.RunWithElevatedPrivileges(delegate
			{
				using (SPSite site = new SPSite(siteId))
				{
					using (SPWeb web = site.OpenWeb(webId))
					{
						SPList list = web.Lists[listId];
						
						SPListItem itemCandidate = list.GetItemById(itemId);

						SPWorkflowCollection workflows = site.WorkflowManager.GetItemActiveWorkflows(itemCandidate);
						SPWorkflow workflow = workflows.OfType<SPWorkflow>()
							.FirstOrDefault(w => w.ParentAssociation.BaseTemplate.Id == templateWorkflowID);

						if (workflow != null)
							SPWorkflowManager.CancelWorkflow(workflow);
					}
				}
			});
		}

		#endregion

		#region ------ Check Workflow -------------------------------------------------------------

		/// <summary>
		/// Checks workflow integrity
		/// </summary>
		/// <param name="web">Parent web</param>
		/// <param name="itemWorkflow">Workflow item</param>
		/// <param name="errorMessage">Error message if check failed</param>
		/// <returns>Returns true if check is passed otherwise returns false</returns>
		public static bool CheckWorkflow(SPWeb web, SPListItem itemWorkflow, out string errorMessage)
		{
			uint language = web.Language;

			// check steps count
			SPListItemCollection itemSteps = GetWorkflowStepItems(web, itemWorkflow);
			if (itemSteps.Count == 0)
			{
				errorMessage = ResourceBuilder.GetResource("HRWorkflowHelper_Error_NoWorkflowSteps", language, itemWorkflow.Title);
				return false;
			}

			// check final decision step
			SPListItem itemStepLast = itemSteps[itemSteps.Count - 1];
			if (!itemStepLast.ContentTypeId.IsChildOf(ContentTypes.FinalDecisionWorkflowStepContentType.ID))
			{
				string contentTypeName = ContentTypes.FinalDecisionWorkflowStepContentType.GetName(language);
				errorMessage = ResourceBuilder.GetResource("HRWorkflowHelper_Error_FinalDecisionIsNotTheLast", language, contentTypeName, itemWorkflow.Title);
				return false;
			}

			errorMessage = null;
			return true;
		}

		#endregion

		#region ------ Emails ---------------------------------------------------------------------

		/// <summary>
		/// Sends email to canidate if an email is set
		/// </summary>
		/// <param name="web">Parent web</param>
		/// <param name="itemCandidate">Item of the candidate</param>
		/// <param name="subjectResourceKey">Resource key for subject of the email message</param>
		/// <param name="bodyResourceKey">Resource key for body of the email message</param>
		public static void SendEmailToCandidate(SPWeb web, SPListItem itemCandidate, string subjectResourceKey, string bodyResourceKey)
		{
			if (!Settings.Default.SendEmailToCandidates)
				return;

			string candidateEmail = itemCandidate.GetValueObject<string>(SiteColumns.CandidateEmailColumn.InternalName);
			
			// check email
			if (string.IsNullOrEmpty(candidateEmail))
			{
				log.Write(LogLevel.Error, "Candidate with '{0}' ID has no email.", itemCandidate.ID);
				return;
			}

			uint? language = itemCandidate.GetValue<uint>(SiteColumns.CommunicationLanguageColumn.InternalName);

			string subject = ResourceBuilder.GetResource(subjectResourceKey, language);
			subject = subject.ReplacePlaceHolders(itemCandidate);

			string body = ResourceBuilder.GetResource(bodyResourceKey, language);
			body = body.ReplacePlaceHolders(itemCandidate);

			SPEmailMessage email = new SPEmailMessage(candidateEmail)
			                       	{
			                       		Subject = subject,
			                       		Body = body
			                       	};
			email.Send(web);
		}

		#endregion
	}
}
